/*

  Copyright (C) 2000, 2001 Silicon Graphics, Inc.  All Rights Reserved.

  This program is free software; you can redistribute it and/or modify it
  under the terms of version 2 of the GNU General Public License as
  published by the Free Software Foundation.

  This program is distributed in the hope that it would be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  

  Further, this software is distributed without any warranty that it is
  free of the rightful claim of any third person regarding infringement 
  or the like.  Any license provided herein, whether implied or 
  otherwise, applies only to this software file.  Patent licenses, if 
  any, provided herein do not apply to combinations of this program with 
  other software, or any other product whatsoever.  

  You should have received a copy of the GNU General Public License along
  with this program; if not, write the Free Software Foundation, Inc., 59
  Temple Place - Suite 330, Boston MA 02111-1307, USA.

  Contact information:  Silicon Graphics, Inc., 1600 Amphitheatre Pky,
  Mountain View, CA 94043, or:

  http://www.sgi.com

  For further information regarding this notice, see:

  http://oss.sgi.com/projects/GenInfo/NoticeExplan

*/


/* ====================================================================
 * ====================================================================
 *
 *
 * Revision history:
 *  20-Aug-96: Original Version, nenad
 *
 * Description: Implementation of demangling utility functions
 *              needed in emitting user-understandable error
 *              messages by the (dsm_)prelinker, linker, and
 *              run-time error checking code generated by LNO.
 *
 * ====================================================================
 * ==================================================================== 
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "dra_demangle.h"


/*
 *  Constants used in (de)mangling
 *
 *  NOTE: These #define's have their 'static const' counterparts
 *        in be/be/dra_internal.h
 *        Any change in this file should also be reflected there. 
 */

#define DRA_MANGLE_SIG      "__nn__"
#define DRA_MANGLE_SIG_LEN  6

#define DRA_STAR_CODE      'S'
#define DRA_BLOCK_CODE     'B'
#define DRA_CYCLIC_CODE    'C'
#define DRA_NDIMS_END      'D'
#define DRA_ESIZE_END      'E'
#define DRA_ARG_SEPARATOR  '_'

#define DRA_STAR_STRING    "*"
#define DRA_BLOCK_STRING   "BLOCK"
#define DRA_CYCLIC_STRING  "CYCLIC"

#define DRA_DEM_BUFSIZE 4096

static char dem_buf[DRA_DEM_BUFSIZE];



/* 
 * Extract only the function name
 */

char* 
DRA_Demangle_Func(const char *mangled_name)
{
  char *func_name;
  char *postfix_sig;
  int   func_name_len;

  /* Check for prefix signature */
  if (mangled_name == NULL ||
      strncmp(mangled_name, DRA_MANGLE_SIG, DRA_MANGLE_SIG_LEN) != 0)
    return NULL;

  func_name = (char*) mangled_name + DRA_MANGLE_SIG_LEN;

  /* Check for postfix signature */
  postfix_sig = strstr(func_name, DRA_MANGLE_SIG);
  
  if (postfix_sig == NULL || 
      postfix_sig[DRA_MANGLE_SIG_LEN] == 0)
    return NULL;

  /* Check that the name is not too long */
  func_name_len = postfix_sig - func_name;
  if (func_name_len >= DRA_DEM_BUFSIZE)
    return NULL;

  /* Copy original function name into static buffer */
  (void) strncpy(dem_buf, func_name, func_name_len);
  dem_buf[func_name_len] = 0;
  
  return dem_buf;
}



/* 
 * Extract and demangle the argument list
 */

char* 
DRA_Demangle_Arglist(const char *mangled_name,
                     const char dim_order)
{
  char *func_name;
  char *postfix_sig;
  char *arg_list;
  char *arg_begin;
  char *arg_end;
  char *arg_dim;
  char *arg_distr;
  char *buf;

  int arg_pos;
  int num_dims;
  int dim;
  int chunk;
  
  /* Dimension ordering must be either 'F' or 'C' */
  if (dim_order != DRA_DIMS_COLUMNWISE && dim_order != DRA_DIMS_ROWWISE)     
    return NULL;
  
  /* Check for prefix signature */
  if (mangled_name == NULL ||
      strncmp(mangled_name, DRA_MANGLE_SIG, DRA_MANGLE_SIG_LEN) != 0)
    return NULL;

  func_name = (char*) mangled_name + DRA_MANGLE_SIG_LEN;

  /* Check for postfix signature */
  postfix_sig = strstr(func_name, DRA_MANGLE_SIG);
  if (postfix_sig == NULL)
    return NULL;

  /* Check that argument list is not empty */
  arg_list = postfix_sig + DRA_MANGLE_SIG_LEN;
  if (arg_list[0] == 0 || arg_list[1] == 0)
    return NULL;
  
  buf = dem_buf;
  *buf++ = '('; 


  for (arg_pos = 0, arg_begin = arg_list; *arg_begin != 0; arg_pos++) {

    /* Underscore must follow each argument */
    arg_end = strchr(arg_begin, DRA_ARG_SEPARATOR);
    if (arg_end == NULL || arg_end == arg_begin) 
      return NULL;

    /* Insert comma before each argument, except for the first one */
    if (arg_pos > 0) 
      *buf++ = ',';

    /* Extract the number of dimensions */
    num_dims = (int) strtol(arg_begin, &arg_begin, 10);

    if (num_dims > 0) {

      /* 'D' must follow the number of dimensions */
      if (*arg_begin++ != DRA_NDIMS_END) 
        return NULL;

      *buf++ = '(';

      /* Skip element size */
      (void) strtol(arg_begin, &arg_begin, 10);
      
      /* 'E' must follow the element size */
      if (*arg_begin++ != DRA_ESIZE_END) 
        return NULL;

      /* For columnwise case (Fortran) decode dimensions in reverse order */
      if (dim_order == DRA_DIMS_COLUMNWISE)
        arg_dim = arg_end;
      else 
        arg_dim = arg_begin;

      /* Go through all dimensions of the reshaped argument */
      for (dim = 0; dim < num_dims; dim++) {
        
        if (dim_order == DRA_DIMS_COLUMNWISE) {
          do {
            arg_dim--;
          } while (*arg_dim != DRA_STAR_CODE    &&
                   *arg_dim != DRA_BLOCK_CODE   &&
                   *arg_dim != DRA_CYCLIC_CODE  &&
                   *arg_dim != DRA_ESIZE_END);
        }

        arg_distr = arg_dim;

        switch(*arg_distr++) {

          case DRA_STAR_CODE:
            if (buf-dem_buf+strlen(DRA_STAR_STRING)+2 >= DRA_DEM_BUFSIZE)
              return NULL;
            buf += sprintf(buf, DRA_STAR_STRING);
            break;

          case DRA_BLOCK_CODE:
            if (buf-dem_buf+strlen(DRA_BLOCK_STRING)+2 >= DRA_DEM_BUFSIZE)
              return NULL;
            buf += sprintf(buf, DRA_BLOCK_STRING);
            break;

          case DRA_CYCLIC_CODE:
            if (buf-dem_buf+strlen(DRA_CYCLIC_STRING)+25 >= DRA_DEM_BUFSIZE)
              return NULL;
            buf += sprintf(buf, DRA_CYCLIC_STRING);
            chunk = (int) strtol(arg_distr, &arg_distr, 10);
            if (chunk != 0) 
              buf += sprintf(buf, "(%d)", chunk);
            else 
              buf += sprintf(buf, "(sym)");
            break;

          default:
            return NULL;
        }

        if (dim == num_dims-1) {
          if (dim_order == DRA_DIMS_ROWWISE && arg_distr != arg_end)
            return NULL;
          if (dim_order == DRA_DIMS_COLUMNWISE && arg_dim != arg_begin)
            return NULL;
          *buf++ = ')';
        }
        else 
          *buf++ = ',';
      }
    }

    /* Non reshaped argument (num_dims == 0) */    
    else { 
      if (buf-dem_buf+2 >= DRA_DEM_BUFSIZE)
        return NULL;
      *buf++ = '-';

      /* Underscore must follow each argument */
      if (*arg_begin != DRA_ARG_SEPARATOR)
        return NULL;
    }

    arg_begin = arg_end+1;
  } 
      
  *buf++ = ')';
  *buf = 0;

  return dem_buf;
}



/*
 * Extract and demangle both the function name and the argument list
 */

char* 
DRA_Demangle(const char *mangled_name,
             const char dim_order)
{
  char *func_name;
  char *postfix_sig;
  char *arg_list;
  char *arg_begin;
  char *arg_end;
  char *arg_dim;
  char *arg_distr;
  char *buf;

  int func_name_len;
  int arg_pos;
  int num_dims;
  int dim;
  int chunk;


  /* Dimension ordering must be either 'F' or 'C' */
  if (dim_order != DRA_DIMS_COLUMNWISE && dim_order != DRA_DIMS_ROWWISE)     
    return NULL;
  
  /* Check for prefix signature */
  if (mangled_name == NULL ||
      strncmp(mangled_name, DRA_MANGLE_SIG, DRA_MANGLE_SIG_LEN) != 0)
    return NULL;

  func_name = (char*) mangled_name + DRA_MANGLE_SIG_LEN;

  /* Check for postfix signature */
  postfix_sig = strstr(func_name, DRA_MANGLE_SIG);
  
  if (postfix_sig == NULL)
    return NULL;

  /* Check that the name is not too long */
  func_name_len = postfix_sig - func_name;
  if (func_name_len + 4 >= DRA_DEM_BUFSIZE)
    return NULL;

  /* Copy original function name into static buffer */
  (void) strncpy(dem_buf, func_name, func_name_len);
  
  /* Check that argument list is not empty */
  arg_list = postfix_sig + DRA_MANGLE_SIG_LEN;

  if (arg_list[0] == 0 || arg_list[1] == 0)
    return NULL;
  
  buf = dem_buf + (postfix_sig-func_name);
  *buf++ = '('; 


  for (arg_pos = 0, arg_begin = arg_list; *arg_begin != 0; arg_pos++) {

    /* Underscore must follow each argument */
    arg_end = strchr(arg_begin, DRA_ARG_SEPARATOR);
    if (arg_end == NULL || arg_end == arg_begin) 
      return NULL;

    /* Insert comma before each argument, except for the first one */
    if (arg_pos > 0) 
      *buf++ = ',';

    /* Extract the number of dimensions */
    num_dims = (int) strtol(arg_begin, &arg_begin, 10);

    if (num_dims > 0) {

      /* 'D' must follow the number of dimensions */
      if (*arg_begin++ != DRA_NDIMS_END) 
        return NULL;

      *buf++ = '(';

      /* Skip element size */
      (void) strtol(arg_begin, &arg_begin, 10);
      
      /* 'E' must follow the element size */
      if (*arg_begin++ != DRA_ESIZE_END) 
        return NULL;

      /* For columnwise case (Fortran) decode dimensions in reverse order */
      if (dim_order == DRA_DIMS_COLUMNWISE)
        arg_dim = arg_end;
      else 
        arg_dim = arg_begin;

      /* Go through all dimensions of the reshaped argument */
      for (dim = 0; dim < num_dims; dim++) {
        
        if (dim_order == DRA_DIMS_COLUMNWISE) {
          do {
            arg_dim--;
          } while (*arg_dim != DRA_STAR_CODE    &&
                   *arg_dim != DRA_BLOCK_CODE   &&
                   *arg_dim != DRA_CYCLIC_CODE  &&
                   *arg_dim != DRA_ESIZE_END);
        }

        arg_distr = arg_dim;

        switch(*arg_distr++) {

          case DRA_STAR_CODE:
            if (buf-dem_buf+strlen(DRA_STAR_STRING)+2 >= DRA_DEM_BUFSIZE)
              return NULL;
            buf += sprintf(buf, DRA_STAR_STRING);
            break;

          case DRA_BLOCK_CODE:
            if (buf-dem_buf+strlen(DRA_BLOCK_STRING)+2 >= DRA_DEM_BUFSIZE)
              return NULL;
            buf += sprintf(buf, DRA_BLOCK_STRING);
            break;

          case DRA_CYCLIC_CODE:
            if (buf-dem_buf+strlen(DRA_CYCLIC_STRING)+25 >= DRA_DEM_BUFSIZE)
              return NULL;
            buf += sprintf(buf, DRA_CYCLIC_STRING);
            chunk = (int) strtol(arg_distr, &arg_distr, 10);
            if (chunk != 0)
              buf += sprintf(buf, "(%d)", chunk);
            else 
              buf += sprintf(buf, "(sym)");
            break;

          default:
            return NULL;
        }

        if (dim == num_dims-1) {
          if (dim_order == DRA_DIMS_ROWWISE && arg_distr != arg_end)
            return NULL;
          if (dim_order == DRA_DIMS_COLUMNWISE && arg_dim != arg_begin)
            return NULL;
          *buf++ = ')';
        }
        else 
          *buf++ = ',';
      }
    }

    /* Non reshaped argument (num_dims == 0) */    
    else { 
      if (buf-dem_buf+2 >= DRA_DEM_BUFSIZE)
        return NULL;

      *buf++ = '-';
      /* Underscore must follow each argument */
      if (*arg_begin != DRA_ARG_SEPARATOR)
        return NULL;
    }

    arg_begin = arg_end+1;
  } 
      
  *buf++ = ')';
  *buf = 0;

  return dem_buf;
}



